#include "opencv/cv.h"
#include "opencv/highgui.h"
#include <stdio.h>
#include <stdlib.h>

#define FPS 15
#define forn(i,n) for(i=0;i<n;i++)

extern void asmRoberts(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmSobel(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmPrewitt(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmFrei(const char* src, char* dst, int ancho, int alto);

extern void asmSobelViejo(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmRobertsViejo(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);
extern void asmPrewittViejo(const char* src, char* dst, int ancho, int alto, int xorder, int yorder);

int main( int argc, char** argv ){
	IplImage * src = 0;
	IplImage * dst = 0;

	char* filename = argc==3?argv[2]:(char*)"lena.bmp";

	if(argc>=2 && strcmp(argv[1],"webcam")){
		if((src = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE))==0) return -1;
		if((dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1))==0) return -1;

		if(!strcmp(argv[1],"r1") || !strcmp(argv[1],"roberts")){
			asmRoberts(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("roberts.bmp", dst);
			printf("%s\n","output written to roberts.bmp");
		
		}else if(!strcmp(argv[1],"r2") || !strcmp(argv[1],"prewitt")){
			asmPrewitt(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("prewitt.bmp", dst);
			printf("%s\n","output written to prewitt.bmp");

		}else if(!strcmp(argv[1],"r3") || !strcmp(argv[1],"sobelx")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0);
			cvSaveImage("sobel_x.bmp", dst);
			printf("%s\n","output written to sobel_x.bmp");
			
		}else if(!strcmp(argv[1],"r4") || !strcmp(argv[1],"sobely")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 0, 1);
			cvSaveImage("sobel_y.bmp", dst);
			printf("%s\n","output written to sobel_y.bmp");
			
		}else if(!strcmp(argv[1],"r5") || !strcmp(argv[1],"sobel")){
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1);
			cvSaveImage("sobel.bmp", dst);
			printf("%s\n","output written to sobel.bmp");

		}else if(!strcmp(argv[1],"r6") || !strcmp(argv[1],"freichen")){
			asmFrei(src->imageData, dst->imageData, src->width, src->height);
			cvSaveImage("frei_chen.jpg", dst);
			printf("%s\n","output written to frei_chen.jpg");

		}else if(!strcmp(argv[1],"time")){

			int it=1000;
			printf("tomando tiempos de asmSobel, asmSobelViejo y cvSobel con %d iteraciones...\n\n",it);
			int tscl;
			long long int totasm=0;
			unsigned int minasm=0;
			int i;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totasm += tscl;
				minasm=!minasm||tscl<minasm?tscl:minasm;
			}
			printf("SobelX:\nPromedio: %.0f\nMinimo: %d\n",(double)totasm/it,minasm);

			/* Ahora la implementacion con GPR */
			printf("\n");
			long long int totAsmViejo=0;
			unsigned int minAsmViejo=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmSobelViejo(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totAsmViejo += tscl;
				minAsmViejo=!minAsmViejo||tscl<minAsmViejo?tscl:minAsmViejo;
			}
			/* Imprimo los resultados */
			printf("SobelX viejo:\nPromedio: %.0f\nMinimo: %d\n",(double)totAsmViejo/it,minAsmViejo);

			/* Ahora la lib openCv */
			printf("\n");
			long long int totcv=0;
			unsigned int mincv=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				cvSobel(src, dst, 1, 0, 3);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totcv += tscl;
				mincv=!mincv||tscl<mincv?tscl:mincv;
			}
			/* Imprimo los resultados */
			printf("cvSobelX:\nPromedio: %.0f\nMinimo: %d\n",(double)totcv/it,mincv);

			/* Sobel con GPR */
			printf("\n");
			long long int totSobelViejo=0;
			unsigned int minSobelViejo=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmSobelViejo(src->imageData, dst->imageData, src->width, src->height, 1, 1);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totSobelViejo += tscl;
				minSobelViejo=!minSobelViejo||tscl<minSobelViejo?tscl:minSobelViejo;
			}
			/* Imprimo los resultados */
			printf("asmSobel viejo:\nPromedio: %.0f\nMinimo: %d\n",(double)totSobelViejo/it,minSobelViejo);

			/* Sobel con SSE */
			printf("\n");
			long long int totSobel=0;
			unsigned int minSobel=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totSobel += tscl;
				minSobel=!minSobel||tscl<minSobel?tscl:minSobel;
			}
			/* Imprimo los resultados */
			printf("asmSobel:\nPromedio: %.0f\nMinimo: %d\n",(double)totSobel/it,minSobel);

			/* Prewitt con GPR */
			printf("\n");
			long long int totPrewittViejo=0;
			unsigned int minPrewittViejo=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmPrewittViejo(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totPrewittViejo += tscl;
				minPrewittViejo=!minPrewittViejo||tscl<minPrewittViejo?tscl:minPrewittViejo;
			}
			/* Imprimo los resultados */
			printf("asmPrewitt viejo:\nPromedio: %.0f\nMinimo: %d\n",(double)totPrewittViejo/it,minPrewittViejo);

			/* Prewitt con SSE */
			printf("\n");
			long long int totPrewitt=0;
			unsigned int minPrewitt=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmPrewitt(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totPrewitt += tscl;
				minPrewitt=!minPrewitt||tscl<minPrewitt?tscl:minPrewitt;
			}
			/* Imprimo los resultados */
			printf("asmPrewitt:\nPromedio: %.0f\nMinimo: %d\n",(double)totPrewitt/it,minPrewitt);

			/* Roberts con GPR */
			printf("\n");
			long long int totRobertsViejo=0;
			unsigned int minRobertsViejo=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmRobertsViejo(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totRobertsViejo += tscl;
				minRobertsViejo=!minRobertsViejo||tscl<minRobertsViejo?tscl:minRobertsViejo;
			}
			/* Imprimo los resultados */
			printf("asmRoberts viejo:\nPromedio: %.0f\nMinimo: %d\n",(double)totRobertsViejo/it,minRobertsViejo);

			/* Roberts con SSE */
			printf("\n");
			long long int totRoberts=0;
			unsigned int minRoberts=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmRoberts(src->imageData, dst->imageData, src->width, src->height, 1, 0);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totRoberts += tscl;
				minRoberts=!minRoberts||tscl<minRoberts?tscl:minRoberts;
			}
			/* Imprimo los resultados */
			printf("asmRoberts:\nPromedio: %.0f\nMinimo: %d\n",(double)totRoberts/it,minRoberts);

			/* Frei - Chen */
			printf("\n");
			long long int totFreichen=0;
			unsigned int minFreichen=0;
			forn(i,it){
				// Tomar estado del TSC antes de iniciar el procesamiento de bordes.
				__asm__ __volatile__ ("rdtsc;mov %%eax,%0" : : "g" (tscl));
				asmFrei(src->imageData, dst->imageData, src->width, src->height);
				// Tomo la medición de tiempo con el TSC y calculo la diferencia. Resultado:
				// Cantidad de clocks insumidos por el algoritmo.
				__asm__ __volatile__ ("rdtsc;sub %0,%%eax;mov %%eax,%0" : : "g" (tscl));
				totFreichen += tscl;
				minFreichen=!minFreichen||tscl<minFreichen?tscl:minFreichen;
			}
			/* Imprimo los resultados */
			printf("asmFreiChen:\nPromedio: %.0f\nMinimo: %d\n",(double)totFreichen/it,minFreichen);

			printf("\n");
			printf("En promedio:\n");
			printf("SobelX Viejo / cvSobelX:  %.4f\n",(double)totAsmViejo/totcv);
			printf("SobelX / SobelX Viejo:  %.4f\n",(double)totasm/totAsmViejo);
			printf("SobelX / cvSobelX:  %.4f\n",(double)totasm/totcv);
			printf("En mínimo:\n");
			printf("SobelX Viejo  / cvSobelX:  %.4f\n",(double)minAsmViejo/mincv);
			printf("SobelX / SobelX Viejo:  %.4f\n",(double)minasm/minAsmViejo);
			printf("SobelX / cvSobelX:  %.4f\n",(double)minasm/mincv);

		}
	}else if(argc>=2 && !strcmp(argv[1],"webcam")){
		/* Esto es algo aparte que implementamos. Aplica los filtros en tiempo real a la entrada de la webcam (de haber una) */
		cvNamedWindow("camara",1);
		CvCapture* capture = (argc==3)?cvCaptureFromAVI(filename):cvCaptureFromCAM(-1);
		if(!capture){printf("%s\n","Error al conectar la camara");return -1;}
		IplImage* frame = cvQueryFrame(capture);
		if((src = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
		if((dst = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1))==0) return -1;
		cvMoveWindow("camara",100,100);

		while(1){
			frame = cvQueryFrame(capture);
			cvCvtColor(frame,src,CV_BGR2GRAY); 										// convierte RGB a Grayscale
			asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1); // aplico la mascara de sobel
			cvShowImage("camara",dst);
			int key = cvWaitKey(1000/FPS);
			if((char)key==27) break;
			else if((char)key=='p'){cvSaveImage("webcam_screenshot.jpg", dst); printf("%s\n","output written to webcam_screenshot.jpg");}
		}
		cvReleaseCapture(&capture);
		cvDestroyWindow("camara");
	}else{
		if((src = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE))==0) return -1;
		if((dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1))==0) return -1;
		asmSobel(src->imageData, dst->imageData, src->width, src->height, 1, 1);
		cvSaveImage("sobel.bmp", dst);
		printf("%s\n","output written to sobel.bmp");
	}
   return 0;
}
